/*
 * MIT License
 * 
 * Copyright (c) 2017 wen.gu <454727014@qq.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
/***************************************************************************
 * Name: tf_thread.c
 *
 * Purpose: a controller(eg. resume, pause) for thread
 *
 * Developer:
 *   wen.gu , 2016-08-08
 *
 * TODO: 
 *
 ***************************************************************************/

#include <string.h>

#include "general_error.h"

#include "tiny_thread.h"

#include "tiny_string.h"

#include "vos_sys.h"
#include "vos_mutex.h"
#include "vos_virtual_watchdog.h"
#include "vos_condition.h"


#include "vos_mem.h"
#include "vos_thread.h"


#define T_LOG_TAG "FRE_THD"
//#include "tf_log.h"



/***************************************************************************
 *
 * macro define
 *
 ***************************************************************************/
#define LOGD printf
#define LOGE printf
#define LOGW printf

/***************************************************************************
 *
 *data struct define
 *
 ***************************************************************************/
typedef enum tf_thread_state_e
{
    TTHD_StateStop = 1,
    TTHD_StatePause,
    TTHD_StateRun,
}tf_thread_state_t;



typedef struct my_thread_s 
{
	tf_thread_id tid;
	tf_thread_state_t state; /** for indicate thread state */
	tf_thread_state_t request_state; /** for request change thread state */

	GPHD lock; /** lock lock */
	GPHD thread_cond; /** thread condition lock, for change thread state */
	GPHD thread_request_cond; /** for request change thread state */

	tfThreadFunc_t looper; /** callback function for thread looper */
	void* opaque; /** the parameter of callback function looper() */

	const char* thd_name; /** the name of current thread */
	unsigned int timeout_ms; /** the value of timeout in milli-second(ms) */
	tf_thread_priority_t priority;
	vosWatchdogLite_t* watchdog; /** watchdog pointer */
	vosWatchdogLite_t storge_watchdog; /** storage watchdog data structure */
}my_thread_t;
/***************************************************************************
*
*  inner API define
*
***************************************************************************/

static inline const char* tfGetStateName(tf_thread_state_t state)
{
	const char* ret = "unknown";
	switch(state)
	{
	case TTHD_StateRun:   ret = "running"; break;
	case TTHD_StatePause: ret = "paused"; break;
	case TTHD_StateStop: ret = "stopped"; break;
	default: break;
	}

	return ret;
}

static GS32 tfThreadLooper(void* param)
{
	if (param)
	{
		my_thread_t* mthd = (my_thread_t*)param;

		mthd->looper(mthd->opaque);
	}
	else
	{
		LOGE("invalid thread parameter\n");
	}
	
	return 0;
}
/***************************************************************************
 *
 *API define
 *
 ***************************************************************************/

//////////////////////////////////////////////////////////////////////////
GPHD tfThreadCreate(const char* name, GU32 timeout_ms, GU32 priority, tfThreadFunc_t looper, void* opaque)
{
      
	my_thread_t* mthd = NULL;
	
	if (looper && opaque)
	{
		mthd = (my_thread_t*)memAlloc(sizeof(my_thread_t));

		if (mthd)
		{
			memset(mthd, 0, sizeof(*mthd));
			mthd->lock = vosMutexCreate();
			GCHECK(mthd->lock);
			mthd->thread_cond = vosCondCreate();
			GCHECK(mthd->thread_cond);
			mthd->thread_request_cond = vosCondCreate();
			GCHECK(mthd->thread_request_cond);
			mthd->looper = looper;
			mthd->state = TTHD_StatePause;
			mthd->request_state = TTHD_StatePause;
			mthd->priority = priority;
			mthd->opaque = opaque;
			mthd->thd_name = name ? strdup(name) : name;
			mthd->tid = TF_INVALID_TID;

			if (timeout_ms > 0)
			{/**initialize watchdog, only when timeout > 0 */
				mthd->watchdog = &(mthd->storge_watchdog);
				if (vosWatchdogLiteInit(mthd->watchdog) != G_OK)
				{
					mthd->watchdog = NULL;
					LOGW("initialize watchdog failed, the function of thread is timeout"
						"will lose efficacy\n");
				}
				else
				{
					/** start watchdog */
					vosWatchdogLiteReset(mthd->watchdog, timeout_ms);
				}
			}
		}
		else
		{
			LOGE("alloc thread controller instance failed\n");
		}
	}
	else
	{
		LOGE("bad parameter, get_tid is NULL\n");
	}
	

	return (GPHD)mthd;
}

void  tfThreadDestroy(GPHD tthd)
{
	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;

		tfThreadStop(tthd);

		if (mthd->lock)
		{
			vosMutexFree(mthd->lock);
		}

		if (mthd->thread_cond)
		{
			vosCondFree(mthd->thread_cond);
		}

		if (mthd->thread_request_cond)
		{
			vosCondFree(mthd->thread_request_cond);
		}

		if (mthd->watchdog)
		{
			vosWatchdogLiteDeinit(mthd->watchdog);
		}

		if (mthd->thd_name)
		{
			free((void*)mthd->thd_name);
		}

		if (mthd->tid != TF_INVALID_TID)
		{
			vosThreadJoin(mthd->tid);
		}

		memFree(tthd);
	}
}


GS32 tfThreadResume(GPHD tthd)
{
	GS32 ret = G_ErrorBadParameter;

	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;
		vosMutexLock(mthd->lock);

		if (mthd->state != TTHD_StateRun)
		{
			if (mthd->tid == TF_INVALID_TID)
			{
				vos_thread_id tid;
				vos_thread_attr_t attr;
				memset(&attr, 0, sizeof(attr));
				attr.name = mthd->thd_name;
				attr.priority = mthd->priority;
				attr.joinable = GTRUE;

				ret = vosThreadCreate(&tid, tfThreadLooper, mthd, &attr);

				if (G_OK == ret)
				{
				    mthd->state = TTHD_StateRun;/**initialize state to running*/
					mthd->tid = tid;
				}
			}
			else
			{
				ret = G_OK;
			}

			if (ret == G_OK)
			{
				mthd->request_state = TTHD_StateRun; /**todo ,something */
				vosCondBroadcast(mthd->thread_cond);		
			}
		}
		else
		{
			ret = G_ErrorInvalidOperation;
		}

		vosMutexUnlock(mthd->lock);
	}

	return ret;
}

GS32 tfThreadPause(GPHD tthd, tf_thread_wait_t wait)
{
	GS32 ret = G_ErrorBadParameter;

	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;
		vosMutexLock(mthd->lock);

		if (mthd->tid != TF_INVALID_TID)
		{
			if (mthd->state == TTHD_StateRun)
			{				
				mthd->request_state = TTHD_StatePause; 

				if (wait == TTHD_WaitStateChange)
				{
					if (mthd->tid == vosThreadGetId())
					{
						LOGE("%s called in thread's loop function and wait state\n"
							"change to pause, this will be lead to endless loop, "
							"it's a Bug\n", __FUNCTION__);
					}

					while (mthd->state != TTHD_StatePause)
					{/** wait thread state really change to pause */
						vosCondWait(mthd->thread_request_cond, mthd->lock);
					}
				}
				
				ret = G_OK;
			}
			else
			{

				LOGE("thread state(%s) not in running,so can't do pause\n", tfGetStateName(mthd->state));	
				ret = G_ErrorInvalidOperation;
			}
		}
		else
		{
			LOGE("thread haven't initialized\n");
			ret = G_ErrorNoInit;
		}

		vosMutexUnlock(mthd->lock);

	}

	return ret;
}

GS32 tfThreadStop(GPHD tthd)
{
	GS32 ret = G_ErrorBadParameter;

	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;
		vosMutexLock(mthd->lock);

		if (mthd->tid != TF_INVALID_TID)
		{
			if (mthd->state != TTHD_StateStop)
			{
				mthd->request_state = TTHD_StateStop;
				vosCondBroadcast(mthd->thread_cond);

				if (mthd->tid == vosThreadGetId())
				{
					LOGE("%s called in thread's loop function and wait state\n"
						"change to pause, this will be lead to endless loop, "
						"it's a Bug\n", __FUNCTION__);
				}

				while (mthd->state != TTHD_StateStop)
				{/** wait thread state really change to stop */
					vosCondWait(mthd->thread_request_cond, mthd->lock);
				}
			}
			else
			{

				LOGE("thread already stopped,this is invalid operation\n");
				ret = G_ErrorInvalidOperation;
			}
		}
		else
		{
			LOGE("thread haven't initialized \n");
			ret = G_ErrorNoInit;
		}

		vosMutexUnlock(mthd->lock);
	}

	return ret;
}


GBOL tfThreadCheckTerminated(GPHD tthd)
{
	GBOL ret = GFALSE;


	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;
		vosWatchdogLite_t* watchdog = mthd->watchdog;
		tf_thread_state_t request_state = mthd->request_state;
		if (watchdog)
		{
			vosWatchdogLiteReset(watchdog, mthd->timeout_ms);
		}

		if (TTHD_StatePause == request_state)
		{

			vosMutexLock(mthd->lock);
			if (watchdog)
			{
				vosWatchdogLiteDisable(watchdog);
			}

			mthd->state = TTHD_StatePause;

			vosCondBroadcast(mthd->thread_request_cond);

			while (TTHD_StatePause == mthd->request_state)
			{
				vosCondWait(mthd->thread_cond, mthd->lock);
			}

			mthd->state = mthd->request_state;
			vosCondBroadcast(mthd->thread_request_cond); /**for the case, request stop after pause */
			
			if (watchdog)
			{
				vosWatchdogLiteEnable(watchdog);
			}

			vosMutexUnlock(mthd->lock);

		}
		else if (TTHD_StateStop == request_state)
		{	/** for the case: request stop at running state */
			vosMutexLock(mthd->lock);
			mthd->state = mthd->request_state;
			vosCondBroadcast(mthd->thread_request_cond);
			vosMutexUnlock(mthd->lock);
		}

		if (TTHD_StateStop == mthd->state)
		{
			ret = GTRUE; /** when this function return ,current thread loop will be exit*/
		}

	}
	else
	{
		ret = GTRUE;
	}

	return ret;
}

GBOL tfThreadIsTimeout(GPHD tthd)
{
	GBOL ret = GFALSE; /** default as doesn't timeout */

	if (tthd)
	{
		my_thread_t* mthd = (my_thread_t*)tthd;

		vosMutexLock(mthd->lock);

		if (mthd->watchdog)
		{
			ret = vosWatchdogLiteIsTimeout(mthd->watchdog);			
		}

		vosMutexUnlock(mthd->lock); /** unlock */
	}

	return ret;
}

tf_thread_id tfThreadGetTid(GPHD tthd)
{
	return tthd ? ((my_thread_t*)tthd)->tid : TF_INVALID_TID;
}